﻿//------------------------------------------------------------------------------------------------- 
// <copyright file="CreateRedirectPage.cs" company="Collaboris Ltd.">
//   Copyright (c) Collaboris Ltd. All rights Reserved.
// </copyright>
// <summary>Publishes a Colleciton of Publishing Pages</summary>
//-------------------------------------------------------------------------------------------------
using Collaboris.Saf.Actions.Moss.Entities;

namespace Collaboris.Saf.Actions.Moss.Publishing
{
    using System.Collections.Generic;

    using Engine;

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Publishing;

    using SharePoint.Moss.Publishing;
    using SharePoint.Wss.Attributes;
    using SharePoint.Wss.Files;
    using SharePoint.Wss.Lists;
    using SharePoint.Wss.Webs;

    using Utils;

    /// <summary>
    /// Creates a new Redirect Page using the SharePoint 
    /// Redirect Page Layout.
    /// </summary>
    [CollaborisClass(
          Title = "Create Redirect Page",
          ReleaseStatus = ReleaseStatus.Stable,
          DisableDocumentation = false,
          ContactAuthorEmail = "Support@Collaboris.co.uk",
          Summary = "Creates a new redirect page using the standard SharePoint redirect page layout.",
          Remarks = "It is also possible to specify a custom redirect page layout and content type if the standard SharePoint one isn't required.",
          ClassGuid = "{156E17A5-A7BA-4e1f-8A48-1BC341A7C616}",
          UndoImplemented = false
      )
    ]
    public class CreateRedirectPage : MOSSActionBase<RedirectPageInfo>
    {
        #region Fields (1)

        /// <summary>
        /// Set up the Action Name 
        /// </summary>
        //private const string ActionName = "Collaboris.SharePoint.Actions.MOSS.Publishing.CreateRedirectPage";

        #endregion Fields

        /// <summary>
        /// </summary>
        private string versionAddedKey = "RedirectVersionNumber";

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="CreateRedirectPage"/> class. 
        /// </summary>
        public CreateRedirectPage()
        {
            this.Scope = SPScope.Web;
        }

        #endregion Constructors

        /*
        /// <summary>
        /// Return the name of this Action
        /// </summary>
        /// <returns></returns>
        public override string Name
        {
            get
            {
                return ActionName;
            }
        }
*/

        #region Protected Methods

        /// <summary>
        /// This Action will remove the Redirect Page.
        /// </summary>
        /// <remarks>
        /// There are a few rules here : 
        ///     - if the page is the Default Page, it will NOT be removed.
        ///     - if there is only one version for the page, the whole file will be removed.
        ///     - if there is more than one version, then the version that this action 
        ///       created will be removed.
        /// </remarks>
        /// <param name="context">
        /// </param>
        /// <param name="entity">
        /// The entity.
        /// </param>
        /// <returns>
        /// </returns>
        protected override ProcessStatus UndoAction(SafContext context, RedirectPageInfo entity)
        {
            this.Trace.TraceMethodStart("UndoAction");

            //ValidationHelper.VerifyObjectArgument(context.Site, "context.Site");

            //using (SPWeb currentWeb = SPWebHelper.GetWeb(context.Site, entity.SiteUrl))
            //{
            //    this.Trace.TraceVerbose("Getting the Publishing Site");
            //    PublishingWeb pubweb = PublishingWeb.GetPublishingWeb(currentWeb);

            //    this.RemoveRedirect(entity, pubweb);
            //}

            this.Trace.TraceMethodEnd("UndoAction");

            return ProcessStatus.Success;
        }

        /// <summary>
        /// This Action creates a redirect page in the given PublishingWeb.
        /// </summary>
        /// <remarks>
        /// There are a few rules here : 
        ///     - if a page already exists a new version is added
        ///     - if the page doesnt exist one is created
        ///     - The page WILL be checked in prior to running.
        /// The page will be checked-in, published and approved after creation.
        /// </remarks>
        /// <param name="context">
        /// </param>
        /// <param name="entity">
        /// The entity.
        /// </param>
        /// <returns>
        /// <see cref="ProcessStatus"/></returns>
        protected override ProcessStatus DoAction(SafContext context, RedirectPageInfo entity)
        {
            this.Trace.TraceMethodStart("DoAction");

            this.Trace.TraceVerbose("Getting the Publishing Site");
            PublishingWeb pubweb = PublishingWeb.GetPublishingWeb(TargetWeb);

            this.CreateRedirect(entity, pubweb);
            
            this.Trace.TraceMethodEnd("DoAction");

            return ProcessStatus.Success;
        }

        #endregion

        /// <summary>
        /// Checks the Data entity for Validity
        /// </summary>
        /// <param name="context">The <see cref="IContext"/>context.</param>
        /// <param name="entity">The data entity to Validate</param>
        /// <param name="errors">List of <see cref="ValidationError"/>s</param>
        /// <param name="mode"><see cref="ProcessMode"/> signalling wheter we are Doing, or Undoing the action.</param>
        /// <returns>false if invalid</returns>
        protected override bool IsValid(SafContext context, RedirectPageInfo entity, ref List<ValidationError> errors, ProcessMode mode)
        {
            int currentCount = errors.Count;

            // Validate each object now.
            ValidateStringForNull(entity.PageName, "RedirectPageInfo.PageName", ref errors);
            ValidateStringForNull(entity.RedirectUrl, "RedirectPageInfo.RedirectUrl", ref errors);
            ValidateNullable(entity.AddVersionIfExists, "RedirectPageInfo.AddVersionIfExists", ref errors);
            ValidateNullable(entity.SetAsDefault, "RedirectPageInfo.SetAsDefault", ref errors);
            
            if (! string.IsNullOrEmpty(entity.CustomContentTypeId))
                this.CheckCustomFieldsForMutuality(context, entity, ref errors, mode);

            if (!string.IsNullOrEmpty(entity.CustomPageLayout))
                this.CheckCustomFieldsForMutuality(context, entity, ref errors, mode);

            if (!string.IsNullOrEmpty(entity.CustomUrlFieldName))
                this.CheckCustomFieldsForMutuality(context, entity, ref errors, mode);

            // Have we added errors ?
            return errors.Count == currentCount;
        }

        /// <summary>
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        /// <param name="entity">
        /// The entity.
        /// </param>
        /// <param name="errors">
        /// The errors.
        /// </param>
        /// <param name="mode">
        /// The mode.
        /// </param>
        private void CheckCustomFieldsForMutuality(SafContext context, RedirectPageInfo entity, ref List<ValidationError> errors, ProcessMode mode)
        {
            ValidateStringForNull(entity.CustomContentTypeId, "RedirectPageInfo.CustomContentTypeId", ref errors);
            ValidateStringForNull(entity.CustomPageLayout, "RedirectPageInfo.CustomPageLayout", ref errors);
            ValidateStringForNull(entity.CustomUrlFieldName, "RedirectPageInfo.CustomUrlFieldName", ref errors);
        }

        #region Private Methods

        /// <summary>
        /// Creates the redirect.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="pubweb">The pubweb.</param>
        private void CreateRedirect(RedirectPageInfo entity, PublishingWeb pubweb)
        {
            this.Trace.TraceVerbose(
                    "Creating the new Redirect Page. " + "RedirectUrl='{0}', " + "RedirectUrlDescription='{1}', " +
                    "SetAsDefault='{2}', ",
                    entity.RedirectUrl,
                    entity.RedirectUrlDescription,
                    entity.SetAsDefault.ToString());

            // store the Version we just added in the prop bag, so we can roll back
            PublishingPage page = PublishingPageHelper.GetPage(entity.PageName, pubweb);
            int currentVersionId = 0;
            if (page != null)
            {
                SPListItemVersion latestVersh = SPListItemHelper.GetLastVersion(page.ListItem);
                if (latestVersh != null)
                    currentVersionId = latestVersh.VersionId;
            }

            PublishingPage redirectPage = PublishingPageHelper.CreateRedirectPage(
                    pubweb,
                    entity.RedirectUrl,
                    entity.RedirectUrlDescription,
                    entity.PageName,
                    entity.SetAsDefault.Value,
                    entity.AddVersionIfExists.Value,
                    entity.CustomContentTypeId,
                    entity.CustomPageLayout,
                    entity.CustomUrlDescFieldName,
                    entity.CustomUrlFieldName);            

            ValidationHelper.VerifyObjectArgument(redirectPage, "redirectPage");

            this.Trace.TraceVerbose("Getting the corresponding file for the Page Layout");
            SPFile file = redirectPage.ListItem.File;
            ValidationHelper.VerifyObjectArgument(file, "file");

            this.Trace.TraceVerbose("Publishing file.");
            SPFileHelper.PublishFile(file, "Published by SAF (CreateRedirectPage) Action", true);

            this.SaveProperty<int>(pubweb.Web, this.CreateListKey(file.Item), currentVersionId);
        }

        /// <summary>
        /// Creates the list key.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        private string CreateListKey(SPListItem item)
        {
            return string.Format("{0}_{1}", this.versionAddedKey, item.ID.ToString());
        }

        /// <summary>
        /// Removes the redirect.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="pubweb">The pubweb.</param>
        private void RemoveRedirect(RedirectPageInfo entity, PublishingWeb pubweb)
        {
            this.Trace.TraceVerbose("Getting the Publishing Page that needs to be Undone.");
            PublishingPage pageToChange = PublishingPageHelper.GetPage(entity.PageName, pubweb);
            ValidationHelper.VerifyObjectArgument(pageToChange, "pageToChange");

            int lastVersionId = this.GetProperty<int>(pubweb.Web, this.CreateListKey(pageToChange.ListItem));

            this.Trace.TraceVerbose(
                    "Deleting this Version number '{0}' for this Publishing Page '{1}'.",
                    lastVersionId,
                    pageToChange.Url);

            ValidationHelper.VerifyIntArgument(lastVersionId, "lastVersionId");

            // Get a handle to the old version
            SPListItemVersion oldVersh = pageToChange.ListItem.Versions.GetVersionFromID(lastVersionId);
            
            // Get a handle to the latest version
            SPListItemVersion latestVersh = SPListItemHelper.GetLastVersion(pageToChange.ListItem);
            
            // Check for nulls.
            ValidationHelper.VerifyObjectArgument(oldVersh, "oldVersion");
            ValidationHelper.VerifyObjectArgument(latestVersh, "latestVersion");

            // Get a handle to the file
            SPFile currentFile = pageToChange.ListItem.File;

            // if its not the latest
            if ((oldVersh.VersionId < latestVersh.VersionId) && oldVersh.VersionId > 0)
            {
                SPFileHelper.CheckOutFile(currentFile);
                pageToChange.ListItem.Versions.RestoreByID(oldVersh.VersionId);
                pageToChange.ListItem.Update();

                if (currentFile.CheckOutStatus != SPFile.SPCheckOutStatus.None)
                {
                    SPFileHelper.CheckInFile(
                            currentFile, "Checked in by the SAF Redirect Page Action", SPCheckinType.MajorCheckIn);
                }
                
            }

            // if its the ONLY version delete it
            if (pageToChange.ListItem.Versions.Count == 1)
            {
                if (pubweb.DefaultPage != pageToChange.ListItem.File)
                {
                    pageToChange.ListItem.Delete();
                }
                else
                {
                    this.Trace.TraceVerbose(
                            "CANNOT Delete this Page (as part of the 'Undo') for "
                            + "this Publishing Page '{0}' as its currently the Welcome Page.",
                            pageToChange.Url);
                }
            }

            pageToChange.ListItem.Update();
        }

        #endregion
    }
}